﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;
using AutoMapper;
using GMS.DataAccessDapper;
using GMS.IServices;
using GMS.Models.Dto;
using GMS.Models.Entity;
using GMS.Models.ResponseDto;
using GMS.Models.SearchDto;
using GMS.Utils;
using Newtonsoft.Json;

namespace GMS.Services
{
    public class MenuInfoService : IMenuInfoService
    {
        private readonly DapperUtil _dapper;
        private readonly IMapper _mapper;
        private readonly RedisUtil _redis;

        public MenuInfoService(DapperUtil dapper, IMapper mapper, RedisUtil redis)
        {
            _dapper = dapper;
            _mapper = mapper;
            _redis = redis;
        }

        /// <summary>
        /// 获取全部数据
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<MenuInfoDto>> GetAllAsync(MenuInfoSearchDto dto)
        {
            string strSql = @"select * from MenuInfo ";
            if (!string.IsNullOrEmpty(dto.MenuName))
            {
                dto.MenuName = $"%{dto.MenuName}%";
                strSql += " where menuName like @menuName ";
            }

            strSql += "order by orderNum asc";

            var entities = await _dapper.QueryMultipleAsync<MenuInfo>(strSql, dto);
            var dtos = _mapper.Map<List<MenuInfoDto>>(entities);
            return await Task.FromResult(dtos);

        }

        public async Task<IEnumerable<MenuInfoResponseDto>> GetTierMenusByUserAsync(Guid userId)
        {
            string strSql = @";WITH bottomData AS
            (
                SELECT DISTINCT d.* FROM(
                SELECT * FROM dbo.UserInfo WHERE Id = @UserId) a
                INNER JOIN dbo.User_Roles b ON a.Id = b.UserId
            INNER JOIN dbo.PowerInfo c ON b.RoleId = c.RoleId
            INNER JOIN dbo.MenuInfo d ON d.Id = c.MenuId
                ),
            menus AS(
                    SELECT * FROM bottomData
                    UNION ALL
                    SELECT b.* FROM bottomData a
                    INNER JOIN dbo.MenuInfo b ON a.ParentId = b.Id
                    WHERE b.ParentId <> '00000000-0000-0000-0000-000000000000'
                )
            SELECT DISTINCT *
                FROM menus
                ORDER BY menus.ParentId,menus.OrderNum asc";

            var entities = await _dapper.QueryMultipleAsync<MenuInfo>(strSql, new { userId });
            var dtos = _mapper.Map<List<MenuInfoDto>>(entities);
            var topEntity = await GetTopLevel();
            var responseDtos = await GetTierAsync(dtos, new List<MenuInfoResponseDto>(), topEntity.Id);
            return await Task.FromResult(responseDtos);
        }


        /// <summary>
        /// 根据ParentId查询MenuId
        /// </summary>
        /// <param name="parentId"></param>
        /// <returns></returns>
        public async Task<IEnumerable<MenuInfo>> GetMenusAsync(Guid parentId)
        {
            string strSql = "select * from MenuInfo where ParentId=@parentId order by orderNum asc";
            if (parentId == Guid.Empty || parentId == null)
            {
                strSql = @"SELECT * FROM dbo.MenuInfo WHERE ParentId=(
            SELECT Id FROM dbo.MenuInfo WHERE ParentId = NULL OR ParentId = '00000000-0000-0000-0000-000000000000')";

            }

            return await _dapper.QueryMultipleAsync<MenuInfo>(strSql, new { parentId = parentId });
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<int> AddAsync(MenuInfoDto dto)
        {
            string strSql = @"INSERT INTO dbo.MenuInfo(Id, MenuName, Path, Component, Icon, OrderNum, ParentId, IsHidden, IsDisable, 
                                        IsDel, CreateId, CreateBy, CreateTime, ModifyId, ModifyBy, ModifyTime, Remark)
                                        Values(
                                            @Id, @MenuName, @Path, @Component, @Icon, @OrderNum, @ParentId, @IsHidden, @IsDisable, 
                                            @IsDel, @CreateId, @CreateBy, @CreateTime, @ModifyId, @ModifyBy, @ModifyTime, @Remark
                                         );
                                         UPDATE dbo.MenuInfo SET HasLeaf=1,ModifyId='586D9525-8C4A-4994-8EA7-82C006EBD088',ModifyBy='admin',ModifyTime=GETDATE() WHERE Id=@parentId";

            dto.ParentId ??= (await GetTopLevel()).Id;
            var entity = _mapper.Map<MenuInfo>(dto);
            entity.Id = GuidHelper.NewSeqGuid();

            EntityHelper<MenuInfo>.SetComm(entity);

            return await _dapper.ExecuteAsync(strSql, entity);
            //strSql= "UPDATE dbo.MenuInfo SET HasLeaf=1 WHERE Id=@id"

        }

        public async Task<int> UpdatedAsync(MenuInfo entity)
        {
            string strSql = @"UPDATE dbo.MenuInfo SET MenuName=@menuName,Path=@path,Component=@component,
            Icon = @icon,OrderNum = @orderNum,IsHidden = @isHidden,ModifyId = '586D9525-8C4A-4994-8EA7-82C006EBD088',
            ModifyBy = 'admin',ModifyTime = GETDATE()
            WHERE Id = @id";
            return await _dapper.ExecuteAsync(strSql, entity);
        }


        /// <summary>
        /// TODO:这里可以使用 MemoryCache() 进行缓存起来 因为一直不会变
        /// </summary>
        /// <returns></returns>
        public async Task<MenuInfo> GetTopLevel()
        {
            if (string.IsNullOrEmpty(await _redis.GetStringAsync("MenuTop")))
            {
                string strSql = "SELECT * FROM dbo.MenuInfo WHERE ParentId = NULL OR ParentId = '00000000-0000-0000-0000-000000000000'";
                var menuInfo = await _dapper.QuerySingleAsync<MenuInfo>(strSql);
                _redis.SetAsync("MenuTop", JsonConvert.SerializeObject(menuInfo), _redis.DistributedCacheEntryOptions);
            }
            var result = await _redis.GetStringAsync("MenuInfo");
            return await Task.FromResult(JsonConvert.DeserializeObject<MenuInfo>(result));
        }

        /// <summary>
        /// 获取层级数据
        /// </summary>
        /// <returns></returns>
        public async Task<List<MenuInfoResponseDto>> GetTierAllAsync()
        {
            var dtos = await GetAllAsync(new MenuInfoSearchDto());
            var parentId = dtos.FirstOrDefault(x => x.ParentId == Guid.Empty).Id;
            var responseDtos = await GetTierAsync(dtos.ToList(), new List<MenuInfoResponseDto>(), parentId);
            return await Task.FromResult(responseDtos);
        }

        public async Task<List<MenuInfoResponseDto>> GetTierAsync()
        {
            var dtos = await GetAllAsync(new MenuInfoSearchDto());
            return await GetTierAsync(dtos.ToList(), new List<MenuInfoResponseDto>(), Guid.Empty);
        }

        /// <summary>
        /// 递归获取tree
        /// </summary>
        /// <param name="dtos"></param>
        /// <param name="responseDtos"></param>
        /// <param name="parentId"></param>
        /// <returns></returns>
        private async Task<List<MenuInfoResponseDto>> GetTierAsync(List<MenuInfoDto> dtos, List<MenuInfoResponseDto> responseDtos, Guid parentId)
        {
            var dtoList = dtos.Where(x => x.ParentId == parentId).ToList();
            foreach (var menuInfoDto in dtoList)
            {
                var responseDto = _mapper.Map<MenuInfoResponseDto>(menuInfoDto);
                responseDtos.Add(responseDto);
                await GetTierAsync(dtos, responseDto.Children, menuInfoDto.Id);
            }

            return responseDtos;
        }

        /// <summary>
        /// 获取层级数据  不包括顶层
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<MenuInfo>> GetLevelAsync()
        {
            string strSql = @"SELECT * FROM dbo.MenuInfo WHERE ParentId=(
            SELECT Id FROM dbo.MenuInfo WHERE ParentId = NULL OR ParentId = '00000000-0000-0000-0000-000000000000')
            ";
            return await _dapper.QueryMultipleAsync<MenuInfo>(strSql);
        }

        public async Task<int> DeleteAsync(Guid id)
        {
            string strSql = "DELETE FROM dbo.MenuInfo WHERE Id=@id";
            return await _dapper.ExecuteAsync(strSql, new { id });
        }

    }
}
